#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include <math.h>
#include <float.h>
#if defined(TEST_TARGET_floatl)
#include <varch/command.h>
#include <varch/unitt.h>
#include <varch/floatl.h>
#else  
#include "init.h"
#include "command.h"
#include "unitt.h"
#include "kern.h"
#include "floatl.h"
#endif

typedef union {
    float float_;
    uint32_t int_;
    struct 
    {
        uint32_t mantissa : 23;
        uint32_t exponent : 8;
        uint32_t sign : 1;
    };
} float_u;
typedef union 
{
    double double_;
    uint64_t int_;
    struct 
    {
        uint64_t mantissa : 52;
        uint64_t exponent : 11;
        uint64_t sign : 1;
    } ;
#if defined(FLOATL_USE_64BITS)
    floatl floatl_;
#endif 
} double_u;

static char buffer[1024] = {0};

static unsigned int b2o(unsigned int bits)
{
    unsigned int group = bits / 10;
    unsigned int index = bits % 10;
    unsigned int offset = 0;
    unsigned int o = 0;

    if      (index < 4) offset = 1;
    else if (index < 7) offset = 2;
    else                offset = 3;

    return group * 3 + offset;
}

// 去除字符串首尾的空白字符
static void trim(char *str) 
{
    int start = 0;
    int end = strlen(str) - 1;

    // 去除字符串开头的空白字符
    while (isspace(str[start])) 
    {
        start++;
    }

    // 去除字符串末尾的空白字符
    while (end >= start && isspace(str[end])) 
    {
        end--;
    }

    // 移动字符到字符串开头
    int i;
    for (i = 0; start <= end; i++, start++) 
    {
        str[i] = str[start];
    }
    str[i] = '\0';
}

// 字符串转换为 double 类型
double double_from_string(const char *str) 
{
    char temp[100];
    strcpy(temp, str);
    trim(temp);

    int len = strlen(temp);
    if (len == 0) 
    {
        return 0.0;
    }

    int sign = 1;
    int i = 0;

    // 处理符号
    if (temp[0] == '-') 
    {
        sign = -1;
        i++;
    } 
    else if (temp[0] == '+') 
    {
        i++;
    }

    // 检查是否为十六进制
    if (len >= i + 2 && temp[i] == '0' && (temp[i + 1] == 'x' || temp[i + 1] == 'X')) 
    {
        i += 2;
        double value = 0.0;
        int point_found = 0;
        double fraction = 1.0;
        int exp_sign = 1;
        int exp_value = 0;

        // 解析十六进制数字部分
        for (; i < len; i++) 
        {
            if (temp[i] == '.') 
            {
                point_found = 1;
                continue;
            } 
            else if (temp[i] == 'p' || temp[i] == 'P') 
            {
                i++;
                if (i < len && temp[i] == '-') 
                {
                    exp_sign = -1;
                    i++;
                } 
                else if (i < len && temp[i] == '+') 
                {
                    i++;
                }
                // 解析十六进制指数部分
                for (; i < len; i++) 
                {
                    if (isdigit(temp[i])) 
                    {
                        exp_value = exp_value * 10 + (temp[i] - '0');
                    } 
                    else 
                    {
                        break;
                    }
                }
                break;
            }

            if (isdigit(temp[i])) 
            {
                value = value * 16 + (temp[i] - '0');
            } 
            else if (temp[i] >= 'a' && temp[i] <= 'f') 
            {
                value = value * 16 + (temp[i] - 'a' + 10);
            } 
            else if (temp[i] >= 'A' && temp[i] <= 'F') 
            {
                value = value * 16 + (temp[i] - 'A' + 10);
            }

            if (point_found) 
            {
                fraction *= 16;
            }
        }

        value /= fraction;
        value *= pow(2, exp_sign * exp_value);
        return sign * value;
    }

    // 处理十进制和指数计数法
    double value = 0.0;
    int point_found = 0;
    double fraction = 1.0;
    int exp_sign = 1;
    int exp_value = 0;

    for (; i < len; i++) 
    {
        if (temp[i] == '.') 
        {
            point_found = 1;
            continue;
        } 
        else if (temp[i] == 'e' || temp[i] == 'E') 
        {
            i++;
            if (i < len && temp[i] == '-') 
            {
                exp_sign = -1;
                i++;
            } 
            else if (i < len && temp[i] == '+') 
            {
                i++;
            }
            // 解析十进制指数部分
            for (; i < len; i++) 
            {
                if (isdigit(temp[i])) 
                {
                    exp_value = exp_value * 10 + (temp[i] - '0');
                } 
                else 
                {
                    break;
                }
            }
            break;
        }

        if (isdigit(temp[i])) 
        {
            value = value * 10 + (temp[i] - '0');
        }

        if (point_found) 
        {
            fraction *= 10;
        }
    }

    value /= fraction;
    value *= pow(10, exp_sign * exp_value);
    return sign * value;
}

static double double_from_uint64(uint64_t value)
{
    double_u v = {.int_ = value};
    return v.double_;
}

float double_to_float(double d) 
{
    // 联合体用于直接操作二进制位
    union { double d; uint64_t u; } du = { .d = d };
    union { float f; uint32_t u; } fu;

    // 提取double的符号、指数、尾数
    uint64_t sign = (du.u >> 63) & 0x1;
    int64_t exp = ((du.u >> 52) & 0x7FF) - 1023;  // 原始指数
    uint64_t mant = du.u & 0x000FFFFFFFFFFFFF;     // 52位尾数

    // 特殊值处理（NaN/Inf）
    if (exp == 1024) {
        fu.u = (sign << 31) | 0x7F800000 | (mant ? 0x7FFFFF : 0);
        return fu.f;
    }

    // 调整指数到float范围（-126~127）
    exp += 127;  // 转换为float偏置指数
    if (exp > 255) { // 上溢返回无穷大
        fu.u = (sign << 31) | 0x7F800000;
        return fu.f;
    } else if (exp < 0) { // 下溢返回0
        fu.u = sign << 31;
        return fu.f;
    }

    // 尾数处理（隐含的1 + 52位尾数）
    uint64_t extended_mant = mant | 0x0010000000000000; // 恢复隐含的1
    uint32_t float_mant = (extended_mant >> 29);        // 保留高23位

    // 舍入处理（检查第29位）
    uint32_t round_bits = extended_mant & 0x1FFFFFFF;
    if (round_bits > 0x10000000 || 
        (round_bits == 0x10000000 && (float_mant & 0x1))) {
        float_mant += 1;
        if (float_mant & 0x00800000) { // 尾数进位导致指数进位
            float_mant >>= 1;
            exp += 1;
            if (exp > 255) { // 指数二次上溢
                fu.u = (sign << 31) | 0x7F800000;
                return fu.f;
            }
        }
    }

    // 组合结果
    fu.u = (sign << 31) | (exp << 23) | (float_mant & 0x007FFFFF);
    return fu.f;
}

// 将 double 的二进制表示转换为 uint64_t
typedef unsigned long long uint64_t;
typedef long long int64_t;

uint64_t double_to_bits(double x) {
    return *((uint64_t*)&x);
}

double bits_to_double(uint64_t bits) {
    return *((double*)&bits);
}

// double_ceil 实现
double double_ceil(double x) {
    uint64_t bits = double_to_bits(x);
    int64_t exponent = ((bits >> 52) & 0x7FF) - 1023;

    if (exponent < 0) {
        // 如果指数为负，说明 x 在 (-1, 1) 之间
        if (x > 0) {
            return 1.0;
        } else if (x == 0.0) {
            return 0.0;
        } else {
            return -0.0;
        }
    }

    int shift = 52 - exponent;
    if (shift <= 0) {
        // 如果 x 已经是整数，直接返回 x
        return x;
    }

    uint64_t mask = (1ULL << shift) - 1;
    uint64_t truncated = bits & ~mask;

    double result = bits_to_double(truncated);
    if (result < x) {
        result += 1.0;
    }

    return result;
}

// double_floor 实现
double double_floor(double x) {
    uint64_t bits = double_to_bits(x);
    int64_t exponent = ((bits >> 52) & 0x7FF) - 1023;

    if (exponent < 0) {
        // 如果指数为负，说明 x 在 (-1, 1) 之间
        if (x >= 0) {
            return 0.0;
        } else {
            return -1.0;
        }
    }

    int shift = 52 - exponent;
    if (shift <= 0) {
        // 如果 x 已经是整数，直接返回 x
        return x;
    }

    uint64_t mask = (1ULL << shift) - 1;
    uint64_t truncated = bits & ~mask;

    double result = bits_to_double(truncated);
    if (result > x) {
        result -= 1.0;
    }

    return result;
}

// double_round 实现
double double_round(double x) {
    uint64_t bits = double_to_bits(x);
    int64_t exponent = ((bits >> 52) & 0x7FF) - 1023;

    if (exponent < 0) {
        // 如果指数为负，说明 x 在 (-1, 1) 之间
        if (x > 0) {
            return (x >= 0.5) ? 1.0 : 0.0;
        } else {
            return (x <= -0.5) ? -1.0 : -0.0;
        }
    }

    int shift = 52 - exponent;
    if (shift <= 0) {
        // 如果 x 已经是整数，直接返回 x
        return x;
    }

    uint64_t mask = (1ULL << shift) - 1;
    uint64_t truncated = bits & ~mask;

    double result = bits_to_double(truncated);
    uint64_t fractional = bits & mask;

    if (fractional >= (1ULL << (shift - 1))) {
        result += 1.0;
    }

    return result;
}

static const char* float_show_raw(float x)
{
    float_u a = {.float_ = x};

    printf("float --------------\r\n");
    printf("a.sign %u\r\n", a.sign);
    printf("a.exponent %d\r\n", (int32_t)((int32_t)a.exponent - 127));

    uint32_t mvalue = a.mantissa;
    for (int i = 23 - 1; i >= 0; i--)
    {
        ((mvalue >> (i)) & 1) ? putchar('1') : putchar('0');
    }
    printf("\r\n");

    return 0;
}

static const char* double_show_raw(double x)
{
    double_u a = {.double_ = x};

    printf("[double]: sign(%u) exponent=%d\r\n", a.sign, (int32_t)((int32_t)a.exponent - 1023));

    uint64_t mvalue = a.mantissa;
    for (int i = 52 - 1; i >= 0; i--)
    {
        ((mvalue >> (i)) & 1) ? putchar('1') : putchar('0');
    }
    printf("\r\n");

    return 0;
}

static const char* floatl_show_raw(floatl a, char *buffer, int size, const char *format)
{
    static char sbuffer[100] = {0};
    
    char *base = sbuffer;
    int index = 0;
    int length = 100;

    printf("[floatl]: sign(%u) exponent=%d\r\n", a.sign, (int32_t)((int32_t)a.exponent - __FLOATL_EXP_MID_VALUE__));

    uint32_t mvalue = a.mantissa;
    for (int i = __FLOATL_MANT_HIGH_BITS__ - 1; i >= 0; i--)
    {
        ((mvalue >> (i)) & 1) ? putchar('1') : putchar('0');
    }
    for (int m = __FLOATL_MANT_PARTS__ - 1; m >= 0; m--)
    {
        mvalue = a.mantissas[m];
        for (int i = 31; i >= 0; i--)
        {
            ((mvalue >> (i)) & 1) ? putchar('1') : putchar('0');
        }
    }
    printf("\r\n");

    return base;
}

static double double_random(void) 
{
    uint64_t raw_bits = 0;
    uint32_t sign = 0;
    uint32_t exponent = 0;
    uint64_t mantissa = 0;
    double result;

    do {
        // 定义一个 64 位无符号整数来模拟 double 的存储
        raw_bits = 0;

        // 随机生成符号位（0 或 1）
        sign = rand() % 2;
        raw_bits |= (uint64_t)sign << 63;

        // 随机生成指数位（范围 0 - 2047）
        exponent = rand() % 2048;
        raw_bits |= (uint64_t)exponent << 52;

        // 随机生成尾数位（范围 0 - 4503599627370495）
        mantissa = (uint64_t)rand() << 32 | rand();
        raw_bits |= mantissa & 0xFFFFFFFFFFFFF;

        // 将 64 位无符号整数转换为 double 类型
        memcpy(&result, &raw_bits, sizeof(double));

    } while (isnan(result) || isinf(result) || (exponent == 0 && mantissa != 0));

    return result;
}

static floatl floatl_random(void)
{
    floatl random = FLOATL_CONST_0;

#if 0
    for (int i = 0; i < __FLOATL_U32_PARTS__; i++) 
    {
        random.u32[i] = rand();
    }
#else 
    double r = double_random();
    memcpy(&random, &r, sizeof(double));
#endif 
    return random;
}

#define DOUBLE_RFLAG_NO_PLUS        0x01
#define DOUBLE_RFLAG_NO_SUB         0x02
#define DOUBLE_RFLAG_NO_ZERO        0x04
#define DOUBLE_RFLAG_NO_WIDTH       0x08
#define DOUBLE_RFLAG_NO_PRECISION   0x10
#define DOUBLE_RFLAG_EXCLUDE_F      0x10
#define DOUBLE_RFLAG_EXCLUDE_A      0x20
#define DOUBLE_RFLAG_EXCLUDE_E      0x40

static void double_random_format(char *format, int size, int flags)
{
    const char baseArray[6] = {'F', 'f', 'A', 'a', 'E', 'e'}; 
    int flag_plus = rand() % 2;
    int flag_sub = rand() % 2;
    int flag_zero = rand() % 2;
    int width = rand() % 50;
    int precision = rand() % 10;
    int base = 0;

    int len = 0;
    char *f = format;

    *f++ = '%';
    if (flag_plus && !(flags & DOUBLE_RFLAG_NO_PLUS)) *f++ = '+';
    if (flag_sub && !(flags & DOUBLE_RFLAG_NO_SUB)) *f++ = '-';
    if (flag_zero && !(flags & DOUBLE_RFLAG_NO_ZERO)) *f++ = '0';
    if (!(flags & DOUBLE_RFLAG_NO_WIDTH))
    {
        len = sprintf(f, "%d", width);
        f += len;
    }
    if (!(flags & DOUBLE_RFLAG_NO_PRECISION))
    {
        *f++ = '.';
        len = sprintf(f, "%d", precision);
        f += len;
    }
    if ((flags & DOUBLE_RFLAG_EXCLUDE_F) && (flags & DOUBLE_RFLAG_EXCLUDE_A) && (flags & DOUBLE_RFLAG_EXCLUDE_E))
    {
        flags |= DOUBLE_RFLAG_EXCLUDE_F;
    }
    while (1)
    {
        base = rand() % sizeof(baseArray);
        if (((flags & DOUBLE_RFLAG_EXCLUDE_F) && (baseArray[base] == 'F' || baseArray[base] == 'f')) || 
            ((flags & DOUBLE_RFLAG_EXCLUDE_A) && (baseArray[base] == 'A' || baseArray[base] == 'a')) || 
            ((flags & DOUBLE_RFLAG_EXCLUDE_E) && (baseArray[base] == 'E' || baseArray[base] == 'e')) 
        ) continue;
        break;
    }
    *f++ = baseArray[base];
    *f = '\0';
}

static int64_t uint64_cmp(uint64_t *v1, uint64_t *v2)
{
    int64_t error = *v1 - *v2;
    if (error < 0) error = -error;
    return error;
}

static int double_equal(double *v1, double *v2)
{
    double_u *u1 = (double_u *)v1;
    double_u *u2 = (double_u *)v2;

    // 都认作0，即为相等
    if (u1->exponent == 0 && u2->exponent == 0) return 1;

    // 允许一位舍入误差
    if (uint64_cmp(&u1->int_, &u2->int_) > 1) return 0;

    return 1;
}

static void double_print(FILE *file, double value)
{
    double_u v = {.double_ = value};
    uint64_t mvalue = v.mantissa;
    fprintf(file, "// [double]<%llu>(%u,%d,%llu){", (uint64_t)v.int_, (uint32_t)v.sign, (int32_t)((int32_t)v.exponent - 1023), (uint64_t)v.mantissa);
    for (int i = 51; i >= 0; i--)
    {
        ((mvalue >> (i)) & 1) ? fputc('1', file) : fputc('0', file);
    }
    fprintf(file, "}\n");
}

/************************************************************************************/
/************************************* Unit Test ************************************/
/************************************************************************************/

// #define EXIT_TEST
extern uint64_t unitt_clock(void);

static void generate_filename(char *filename, uint32_t size) 
{
    time_t current_time = time(NULL);
    if (current_time == ((time_t)-1)) 
    {
        fprintf(stderr, "Failed to get current time\n");
        return;
    }
    
    struct tm *local_time = localtime(&current_time);
    if (local_time == NULL) {
        fprintf(stderr, "Failed to convert to local time\n");
        return;
    }
    
    strftime(filename, size, "%Y%m%d_%H%M%S.txt", local_time);
}

static FILE** error_file(int index)
{
    static FILE *table[6] = {NULL, NULL, NULL, NULL}; // stdout
    FILE **file = NULL;

    index = index % 6;
    file = &table[index];
    if (!*file)
    {
        char filename[64] = "built/";
        filename[6] = '0' + index;
        filename[7] = '_';
        generate_filename(filename + 8, sizeof(filename) - 8);
        *file = fopen(filename, "a");
        if (*file == NULL) 
        {
            perror("Failed to open output file");
            return NULL;
        }
    }

    return file;
}

static void error_record_cal(char op, floatl a, floatl b, floatl c, double x, double y, double z)
{
    FILE **file = NULL;
    int index = 0;

    if      (op == '+') index = 0;
    else if (op == '-') index = 1;
    else if (op == '*') index = 2;
    else if (op == '/') index = 3;
 
    file = error_file(index);

    uint64_t m, n;
    memcpy(&m, &a, sizeof(m));
    memcpy(&n, &b, sizeof(n));
    double_print(*file, x);
    double_print(*file, y);
    double_print(*file, z);
    double_print(*file, *(double *)(&c));
    fprintf(*file, "{.v1.int_ = 0x%016llX, .v2.int_ = 0x%016llX}, // %lf %c %lf = %lf\n\n", m, n, x, op, y, z);
}

static void error_record_print(floatl a, char *format, char *s1, char *s2)
{
    FILE **file = error_file(4);

    double_print(*file, *(double *)(&a));
    fprintf(*file, "// %s\n", format);
    fprintf(*file, "// %s\n", s1);
    fprintf(*file, "// %s\n", s2);
    fprintf(*file, "\n\n");
}

static void error_record_from(floatl a, floatl b, char *format, char *s1, char *s2)
{
    FILE **file = error_file(5);

    double_print(*file, *(double *)(&a));
    double_print(*file, *(double *)(&b));
    fprintf(*file, "// %s\n", format);
    fprintf(*file, "// %s\n", s1);
    fprintf(*file, "// %s\n", s2);
    fprintf(*file, "\n\n");
}

static int u_test_add(void)
{
    for (int i = 0; i < 1000; i++)
    {
        floatl a = floatl_random();
        floatl b = floatl_random();
        floatl c = FLOATL_CONST_0;
        
        double x = 0;
        double y = 0;
        double z = 0;

        memcpy(&x, &a, sizeof(x));
        memcpy(&y, &b, sizeof(y));

        { c = floatl_add(a, b);        z = x + y; }
        
        if (!double_equal(&c, &z))
        {
            error_record_cal('+', a, b, c, x, y, z);
            #if defined (EXIT_TEST)
            exit(0);
            #endif 
            return UNITT_E_FAIL;
        }
    }
    
    return UNITT_E_OK;
}

static int u_test_sub(void)
{
    for (int i = 0; i < 1000; i++)
    {
        floatl a = floatl_random();
        floatl b = floatl_random();
        floatl c = FLOATL_CONST_0;
        
        double x = 0;
        double y = 0;
        double z = 0;

        memcpy(&x, &a, sizeof(x));
        memcpy(&y, &b, sizeof(y));

        { c = floatl_sub(a, b);        z = x - y; }
        
        if (!double_equal(&c, &z))
        {
            error_record_cal('-', a, b, c, x, y, z);
            #if defined (EXIT_TEST)
            exit(0);
            #endif 
            return UNITT_E_FAIL;
        }
    }
    
    return UNITT_E_OK;
}

static int u_test_mul(void)
{
    for (int i = 0; i < 1000; i++)
    {
        floatl a = floatl_random();
        floatl b = floatl_random();
        floatl c = FLOATL_CONST_0;
        
        double x = 0;
        double y = 0;
        double z = 0;

        memcpy(&x, &a, sizeof(x));
        memcpy(&y, &b, sizeof(y));

        { c = floatl_mul(a, b);        z = x * y; }
        
        if (!double_equal(&c, &z))
        {
            error_record_cal('*', a, b, c, x, y, z);
            #if defined (EXIT_TEST)
            exit(0);
            #endif 
            return UNITT_E_FAIL;
        }
    }
    
    return UNITT_E_OK;
}

static int u_test_div(void)
{
    for (int i = 0; i < 1000; i++)
    {
        floatl a = floatl_random();
        floatl b = floatl_random();
        floatl c = FLOATL_CONST_0;
        
        double x = 0;
        double y = 0;
        double z = 0;

        memcpy(&x, &a, sizeof(x));
        memcpy(&y, &b, sizeof(y));

        if (fabs(y) < 1e-9) continue;

        { c = floatl_div(a, b);        z = x / y; }
        
        if (!double_equal(&c, &z))
        {
            error_record_cal('/', a, b, c, x, y, z);
            #if defined (EXIT_TEST)
            exit(0);
            #endif 
            return UNITT_E_FAIL;
        }
    }
    
    return UNITT_E_OK;
}

static int u_test_print(void)
{
    static char format[100] = {0};
    static char buffer_f[100] = {0};
    static char buffer_d[100] = {0};

    for (int i = 0; i < 1000; i++)
    {
        floatl a = floatl_random();
        double x = 0;
        int len_f = 0;
        int len_d = 0;

        memcpy(&x, &a, sizeof(x));
        double_random_format(format, sizeof(format), DOUBLE_RFLAG_EXCLUDE_F | DOUBLE_RFLAG_EXCLUDE_A);

        len_f = floatl_print(a, buffer_f, sizeof(buffer_f), format);
        len_d = snprintf(buffer_d, sizeof(buffer_d), format, x);

        if (len_f <= 0 || len_d <= 0) continue;
        
        if (len_f != len_d || strcmp(buffer_f, buffer_d) != 0)
        {
            error_record_print(a, format, buffer_f, buffer_d);
            #if defined (EXIT_TEST)
            exit(0);
            #endif 
            return UNITT_E_FAIL;
        }
    }
    
    return UNITT_E_OK;
}

static int u_test_from(void)
{
    static char format[100] = {0};
    static char buffer_0[100] = {0};
    static char buffer_1[100] = {0};

    for (int i = 0; i < 1000; i++)
    {
        floatl a = floatl_random();
        floatl b = FLOATL_CONST_0;
        
        double_random_format(format, sizeof(format), DOUBLE_RFLAG_NO_PLUS | DOUBLE_RFLAG_NO_SUB | DOUBLE_RFLAG_NO_ZERO | DOUBLE_RFLAG_NO_WIDTH);

        if (floatl_print(a, buffer_0, sizeof(buffer_0), format) <= 0) continue;

        b = floatl_from(buffer_0);

        floatl_print(b, buffer_1, sizeof(buffer_1), format);
        
        if (floatl_isnan(b) || strncmp(buffer_0, buffer_1, sizeof(buffer_0)))
        {
            error_record_from(a, b, format, buffer_0, buffer_1);
            #if defined (EXIT_TEST)
            exit(0);
            #endif 
            return UNITT_E_FAIL;
        }
    }
    
    return UNITT_E_OK;
}

static void unitt_task(void)
{
    static UNITT_TCASE rand_tests[] = {
#if defined(FLOATL_USE_64BITS)
        UNITT_TCASE(u_test_add),
        UNITT_TCASE(u_test_sub),
        UNITT_TCASE(u_test_mul),
        UNITT_TCASE(u_test_div),
        UNITT_TCASE(u_test_print),
#endif
        UNITT_TCASE(u_test_from),
    };

    static UNITT suites[] = {
        { "floatl suite", rand_tests, sizeof(rand_tests) / sizeof(rand_tests[0]) , unitt_clock },
    };

    UNITT_EXE(suites);
}

/************************************************************************************/
/************************************* Base Test ************************************/
/************************************************************************************/

/*----------------------------------------------------------------------------------*/
/*                          Generate configuration [START]                          */
/*----------------------------------------------------------------------------------*/

typedef struct 
{
    uint32_t buffer[256];

    uint32_t FLOATL_BIT_PARTS__;          // bits
    uint32_t FLOATL_U32_PARTS__;          // FLOATL_BIT_PARTS__ / 32 ---- FLOATL_BIT_PARTS__ >> 5
    uint32_t FLOATL_U16_PARTS__;          // FLOATL_BIT_PARTS__ / 16 ---- FLOATL_BIT_PARTS__ >> 4
    uint32_t FLOATL_EXP_BITS__;           // floatl_cfg_gen_exp(FLOATL_BIT_PARTS__)
    uint32_t FLOATL_MANT_BITS__;          // FLOATL_BIT_PARTS__ - FLOATL_EXP_BITS__ - 1
    uint32_t FLOATL_MANT_PARTS__;         // FLOATL_MANT_BITS__ / 32 ---- FLOATL_MANT_BITS__ >> 5 
    uint32_t FLOATL_MANT_HIGH_BITS__;     // FLOATL_MANT_BITS__ % 32 ---- FLOATL_MANT_BITS__ & 0x1F
    uint32_t FLOATL_EXP_MID_VALUE__;      // 2^(FLOATL_EXP_BITS__-1) - 1
    uint32_t FLOATL_EXP_WHL_VALUE__;      // 2^(FLOATL_EXP_BITS__) - 1
    
    uint32_t FLOATL2_BIT_PARTS__;         // FLOATL_BIT_PARTS__ * 2
    uint32_t FLOATL2_U32_PARTS__;         // FLOATL2_BIT_PARTS__ / 32 ---- FLOATL2_BIT_PARTS__ >> 5
    uint32_t FLOATL2_U16_PARTS__;         // FLOATL2_BIT_PARTS__ / 16 ---- FLOATL2_BIT_PARTS__ >> 4

    uint32_t FLOATL_MANT_DIG__;           // FLOATL_MANT_BITS__ + 1
    int32_t  FLOATL_MIN_EXP__;            // - FLOATL_EXP_MID_VALUE__ + 2
    uint32_t FLOATL_MAX_EXP__;            // FLOATL_EXP_MID_VALUE__ + 1
    uint32_t FLOATL_DIG__;                // floor(log10(2) * FLOATL_MANT_DIG__)
    uint32_t FLOATL_DECIMAL_DIG__;        // ceil(log10(2) * FLOATL_MANT_DIG__) + 1
    int32_t  FLOATL_MIN_10_EXP__;         // ceil(log10(2) * (1 - FLOATL_EXP_MID_VALUE__))
    uint32_t FLOATL_MAX_10_EXP__;         // - FLOATL_MIN_10_EXP__ + 1
} FLINFO;

typedef struct 
{
    uint32_t exp;
    char *mantBin;
} FLCSTE; // const pow10(n)

static FLCSTE fle_list[] = {
    {.exp = 0,  .mantBin = "0000000000000000000000000000000000000000000000000000"},
    {.exp = 3,  .mantBin = "0100000000000000000000000000000000000000000000000000"},
    {.exp = 6,  .mantBin = "1001000000000000000000000000000000000000000000000000"},
    {.exp = 9,  .mantBin = "1111010000000000000000000000000000000000000000000000"},
    {.exp = 13, .mantBin = "0011100010000000000000000000000000000000000000000000"},
    {.exp = 16, .mantBin = "1000011010100000000000000000000000000000000000000000"},
    {.exp = 19, .mantBin = "1110100001001000000000000000000000000000000000000000"},
    {.exp = 23, .mantBin = "0011000100101101000000000000000000000000000000000000"},
    {.exp = 26, .mantBin = "0111110101111000010000000000000000000000000000000000"},
    {.exp = 29, .mantBin = "1101110011010110010100000000000000000000000000000000"},
    {.exp = 33, .mantBin = "0010101000000101111100100000000000000000000000000000"},
    {.exp = 36, .mantBin = "0111010010000111011011101000000000000000000000000000"},
    {.exp = 39, .mantBin = "1101000110101001010010100010000000000000000000000000"},
    {.exp = 43, .mantBin = "0010001100001001110011100101010000000000000000000000"},
    {.exp = 46, .mantBin = "0110101111001100010000011110100100000000000000000000"},
    {.exp = 49, .mantBin = "1100011010111111010100100110001101000000000000000000"},
};

// e = 2.8625 * log2(bits) + 0.0111 * bits - 6.6730
uint32_t floatl_cfg_gen_exp(uint32_t bits)
{
    const int y = bits;
    int mantissa = 0;
    int exponent = 0;
    int sign = 1;

    for (int i = y; i > 0; i--)
    {
        if (log2(i) * 2 < (y - i - 1))
        {
            mantissa = i + 1;
            break;
        }
    }
    exponent = bits - sign - mantissa;

    // printf("---------------- %d\r\n", bits);
    // printf("mantissa %d\r\n", mantissa);
    // printf("exponent %d\r\n", exponent);
    // printf("sign %d\r\n", sign);

    // printf("high %d\r\n", mantissa & 0x1F); // % 32
    // printf("part %d\r\n", mantissa >> 5);   // / 32

    if (exponent >= 31) return 0;

    return exponent;
}
 
void floatl_cfg_const_out(FILE* file, FLINFO *info)
{
    if (!file || !info) return;

    fprintf(file, "(floatl){.u32={");
    for (int i = 0; i < info->FLOATL_U32_PARTS__; i++)
    {
        uint32_t value = info->buffer[i];
        if (value == 0)                 fprintf(file, "0,");
        else if (value == 0xFFFFFFFF)   fprintf(file, "-1,");
        else                            fprintf(file, "0x%08X,", value);
    }
    fprintf(file, "}}\n");
}

int floatl_cfg_info_init(FLINFO *info, unsigned int bits)
{
    if ((bits & (bits - 1)) != 0) return 0;

    info->FLOATL_BIT_PARTS__             = bits;
    info->FLOATL_U32_PARTS__             = info->FLOATL_BIT_PARTS__ >> (5);
    info->FLOATL_U16_PARTS__             = info->FLOATL_BIT_PARTS__ >> (4);
    info->FLOATL_EXP_BITS__              = floatl_cfg_gen_exp(info->FLOATL_BIT_PARTS__);
    info->FLOATL_MANT_BITS__             = info->FLOATL_BIT_PARTS__ - info->FLOATL_EXP_BITS__ - 1;
    info->FLOATL_MANT_PARTS__            = info->FLOATL_MANT_BITS__ >> 5;
    info->FLOATL_MANT_HIGH_BITS__        = info->FLOATL_MANT_BITS__ & 0x1F;
    info->FLOATL_EXP_MID_VALUE__         = (uint32_t)pow(2, info->FLOATL_EXP_BITS__ - 1) - 1;
    info->FLOATL_EXP_WHL_VALUE__         = (uint32_t)pow(2, info->FLOATL_EXP_BITS__) - 1;
    info->FLOATL2_BIT_PARTS__            = info->FLOATL_BIT_PARTS__ * 2;
    info->FLOATL2_U32_PARTS__            = info->FLOATL2_BIT_PARTS__ >> (5);
    info->FLOATL2_U16_PARTS__            = info->FLOATL2_BIT_PARTS__ >> (4);
    info->FLOATL_MANT_DIG__              = info->FLOATL_MANT_BITS__ + 1;
    info->FLOATL_MIN_EXP__               = - info->FLOATL_EXP_MID_VALUE__ + 2;
    info->FLOATL_MAX_EXP__               = info->FLOATL_EXP_MID_VALUE__ + 1;
    info->FLOATL_DIG__                   = floor(log10(2) * info->FLOATL_MANT_DIG__);
    info->FLOATL_DECIMAL_DIG__           = ceil(log10(2) * info->FLOATL_MANT_DIG__) + 1;
    info->FLOATL_MIN_10_EXP__            = ceil(log10(2) * (int32_t)(1 - info->FLOATL_EXP_MID_VALUE__));
    info->FLOATL_MAX_10_EXP__            = - info->FLOATL_MIN_10_EXP__ + 1;

    memset(info->buffer, 0, info->FLOATL_U32_PARTS__ * sizeof(uint32_t));

    return 1;
}

FLINFO *floatl_cfg_set_sign(FLINFO *info, int sign)
{
    if (sign) info->buffer[info->FLOATL_U32_PARTS__ - 1] |= (1 << 31);
    else      info->buffer[info->FLOATL_U32_PARTS__ - 1] &= ~(1 << 31);
    return info;
}

FLINFO *floatl_cfg_set_exp_real(FLINFO *info, uint32_t t)
{
    uint32_t temp = 1;

    temp <<= info->FLOATL_EXP_BITS__;
    temp -= 1;

    t &= temp;
    t <<= (32 - info->FLOATL_EXP_BITS__ - 1);

    temp <<= (32 - info->FLOATL_EXP_BITS__ - 1);


    info->buffer[info->FLOATL_U32_PARTS__ - 1] &= (~temp); // clear

    info->buffer[info->FLOATL_U32_PARTS__ - 1] |= t;

    return info;
}

FLINFO *floatl_cfg_set_exp(FLINFO *info, int32_t e)
{
    return floatl_cfg_set_exp_real(info, e + info->FLOATL_EXP_MID_VALUE__);
}

FLINFO *floatl_cfg_set_mant(FLINFO *info, char *bin)
{
    char *s = bin;
    
    while (*s)
    {
        int index = info->FLOATL_MANT_BITS__ - (s - bin) - 1;
        if (index < 0) break;
        if (index >= 0 && *s == '1')
        {
            int part = index / 32;
            int bit = index % 32;
            
            info->buffer[part] |= (1 << bit);
        }
        s++;
    }

    return info;
}

FLINFO *floatl_cfg_int_zero(FLINFO *info)
{
    memset(info->buffer, 0, info->FLOATL_U32_PARTS__ * sizeof(uint32_t));

    return info;
}

FLINFO *floatl_cfg_const_0(FLINFO *info)
{
    memset(info->buffer, 0, info->FLOATL_U32_PARTS__ * sizeof(uint32_t));

    return info;
}

FLINFO *floatl_cfg_const_1(FLINFO *info)
{
    memset(info->buffer, 0, info->FLOATL_U32_PARTS__ * sizeof(uint32_t));

    floatl_cfg_set_exp_real(info, 0xFFFFFFFF);
    info->buffer[info->FLOATL_U32_PARTS__ - 1] &= 0xBFFFFFFF;

    return info;
}

FLINFO *floatl_cfg_const_1eX(FLINFO *info, uint32_t e)
{
    if (e >= sizeof(fle_list) / sizeof(fle_list[0])) return NULL;

    memset(info->buffer, 0, info->FLOATL_U32_PARTS__ * sizeof(uint32_t));

    floatl_cfg_set_exp(info, fle_list[e].exp);
    floatl_cfg_set_mant(info, fle_list[e].mantBin);

    return info;
}

FLINFO *floatl_cfg_const_pi(FLINFO *info)
{
    memset(info->buffer, 0, info->FLOATL_U32_PARTS__ * sizeof(uint32_t));

    floatl_cfg_set_exp(info, 1);
    floatl_cfg_set_mant(info, FLOATL_CFG_MANT_PI);

    return info;
}

FLINFO *floatl_cfg_const_e(FLINFO *info)
{
    memset(info->buffer, 0, info->FLOATL_U32_PARTS__ * sizeof(uint32_t));

    floatl_cfg_set_exp(info, 1);
    floatl_cfg_set_mant(info, FLOATL_CFG_MANT_E);

    return info;
}

FLINFO *floatl_cfg_inf(FLINFO *info)
{
    memset(info->buffer, 0, info->FLOATL_U32_PARTS__ * sizeof(uint32_t));

    floatl_cfg_set_exp_real(info, 0xFFFFFFFF);

    return info;
}

FLINFO *floatl_cfg_nan(FLINFO *info)
{
    memset(info->buffer, 0, info->FLOATL_U32_PARTS__ * sizeof(uint32_t));

    floatl_cfg_set_exp_real(info, 0xFFFFFFFF);
    info->buffer[info->FLOATL_U32_PARTS__ - 1] |= 0x80000000;

    return info;
}

FLINFO *floatl_cfg_all_sign(FLINFO *info)
{
    memset(info->buffer, 0, info->FLOATL_U32_PARTS__ * sizeof(uint32_t));

    info->buffer[info->FLOATL_U32_PARTS__ - 1] |= 0x80000000;

    return info;
}

FLINFO *floatl_cfg_all_exp(FLINFO *info)
{
    memset(info->buffer, 0, info->FLOATL_U32_PARTS__ * sizeof(uint32_t));

    floatl_cfg_set_exp_real(info, 0xFFFFFFFF);

    return info;
}

FLINFO *floatl_cfg_all_mant(FLINFO *info)
{
    memset(info->buffer, -1, info->FLOATL_U32_PARTS__ * sizeof(uint32_t));

    floatl_cfg_set_exp_real(info, 0);
    info->buffer[info->FLOATL_U32_PARTS__ - 1] &= (~0x80000000);

    return info;
}

FLINFO *floatl_cfg_all_exp_mant(FLINFO *info)
{
    memset(info->buffer, -1, info->FLOATL_U32_PARTS__ * sizeof(uint32_t));

    info->buffer[info->FLOATL_U32_PARTS__ - 1] &= (~0x80000000);

    return info;
}

FLINFO *floatl_cfg_hide_mant_bit(FLINFO *info)
{
    memset(info->buffer, 0, info->FLOATL_U32_PARTS__ * sizeof(uint32_t));

    floatl_cfg_set_exp_real(info, 1);

    return info;
}

FLINFO *floatl_cfg_mant_plus_max(FLINFO *info)
{
    memset(info->buffer, 0, info->FLOATL_U32_PARTS__ * sizeof(uint32_t));

    floatl_cfg_set_exp_real(info, 8);

    return info;
}

FLINFO *floatl_cfg_mant_whole(FLINFO *info)
{
    memset(info->buffer, -1, info->FLOATL_U32_PARTS__ * sizeof(uint32_t));

    floatl_cfg_set_exp_real(info, 1);
    info->buffer[info->FLOATL_U32_PARTS__ - 1] &= (~0x80000000);

    return info;
}

FLINFO *floatl_cfg_min(FLINFO *info)
{
    memset(info->buffer, 0, info->FLOATL_U32_PARTS__ * sizeof(uint32_t));

    floatl_cfg_set_exp_real(info, 1);

    return info;
}

FLINFO *floatl_cfg_max(FLINFO *info)
{
    memset(info->buffer, -1, info->FLOATL_U32_PARTS__ * sizeof(uint32_t));

    floatl_cfg_set_sign(info, 0);
    floatl_cfg_set_exp_real(info, 0xFFFFFFFE);

    return info;
}

FLINFO *floatl_cfg_epsilon(FLINFO *info)
{
    memset(info->buffer, 0, info->FLOATL_U32_PARTS__ * sizeof(uint32_t));

    info->buffer[0] |= 1;

    return info;
}

void floatl_cfg_generate(uint32_t bits, const char *filename)
{
    #define MINBITS 64
    #define NEWLINE "\n" // "\r\n" // 

    FILE* output = stdout; // Default output to standard output
    if (filename != NULL) 
    {
        output = fopen(filename, "w");
        if (output == NULL) 
        {
            fprintf(stdout, "[ERROR] Failed to open output file"NEWLINE);
            return;
        }
    }

    /* The number of bits in an floatl needs to be an exponent of two */
    if ((bits & (bits - 1)) != 0)
    {
        fprintf(stdout, "[ERROR] `bits` not a power of 2"NEWLINE);
        return;
    }

    /* Enable PI and E mant configurations */
    if (!FLOATL_CFG_MANT_PI || !FLOATL_CFG_MANT_E)
    {
        fprintf(stdout, "[ERROR] Enable `FLOATL_CFG_MANT_PI` and `FLOATL_CFG_MANT_E` in `floatl_cfg.h` file"NEWLINE);
        return;
    }

    /* Limiting the minimum bit */
    if (bits < MINBITS)
    {
        fprintf(stdout, "[ERROR] `bits` too small\r\b");
        return;
    }

    fprintf(output, "/*****************************************************************/"NEWLINE);
    fprintf(output, "/*                          Config start                         */"NEWLINE);
    fprintf(output, "/*****************************************************************/"NEWLINE);

    for (uint32_t tb = MINBITS; tb <= bits; tb <<= 1)
    {
        if (tb == MINBITS) 
        {
            fprintf(output, "#define FLOATL_USE_%uBITS"NEWLINE, tb);
        }
        else  
        {
            fprintf(output, "// #define FLOATL_USE_%uBITS"NEWLINE, tb);
        }
    }
    fprintf(output, NEWLINE);

    floatl temp;

    for (uint32_t tb = MINBITS; tb <= bits; tb <<= 1)
    {
        if (tb == MINBITS) 
        {
            fprintf(output, "#if defined(FLOATL_USE_%uBITS)"NEWLINE, tb);
        }
        else  
        {
            fprintf(output, "#elif defined(FLOATL_USE_%uBITS)"NEWLINE, tb);
        }

        FLINFO info;
        floatl_cfg_info_init(&info, tb);

        fprintf(output, "#define __FLOATL_BIT_PARTS__                    %u"NEWLINE, info.FLOATL_BIT_PARTS__      );
        fprintf(output, "#define __FLOATL_U32_PARTS__                    %u"NEWLINE, info.FLOATL_U32_PARTS__      );
        fprintf(output, "#define __FLOATL_U16_PARTS__                    %u"NEWLINE, info.FLOATL_U16_PARTS__      );
        fprintf(output, "#define __FLOATL_EXP_BITS__                     %u"NEWLINE, info.FLOATL_EXP_BITS__       );
        fprintf(output, "#define __FLOATL_MANT_BITS__                    %u"NEWLINE, info.FLOATL_MANT_BITS__      );
        fprintf(output, "#define __FLOATL_MANT_PARTS__                   %u"NEWLINE, info.FLOATL_MANT_PARTS__     );
        fprintf(output, "#define __FLOATL_MANT_HIGH_BITS__               %u"NEWLINE, info.FLOATL_MANT_HIGH_BITS__ );
        fprintf(output, "#define __FLOATL_EXP_MID_VALUE__                %u"NEWLINE, info.FLOATL_EXP_MID_VALUE__  );
        fprintf(output, "#define __FLOATL_EXP_WHL_VALUE__                %u"NEWLINE, info.FLOATL_EXP_WHL_VALUE__  );

        fprintf(output, "#define __FLOATL2_BIT_PARTS__                   %u"NEWLINE, info.FLOATL2_BIT_PARTS__     );
        fprintf(output, "#define __FLOATL2_U32_PARTS__                   %u"NEWLINE, info.FLOATL2_U32_PARTS__     );
        fprintf(output, "#define __FLOATL2_U16_PARTS__                   %u"NEWLINE, info.FLOATL2_U16_PARTS__     );

        fprintf(output, "#define __FLOATL_MANT_DIG__                     %u"NEWLINE, info.FLOATL_MANT_DIG__       );
        fprintf(output, "#define __FLOATL_MIN_EXP__                      %d"NEWLINE, info.FLOATL_MIN_EXP__        );
        fprintf(output, "#define __FLOATL_MAX_EXP__                      %u"NEWLINE, info.FLOATL_MAX_EXP__        );
        fprintf(output, "#define __FLOATL_DIG__                          %u"NEWLINE, info.FLOATL_DIG__            );
        fprintf(output, "#define __FLOATL_DECIMAL_DIG__                  %u"NEWLINE, info.FLOATL_DECIMAL_DIG__    );
        fprintf(output, "#define __FLOATL_MIN_10_EXP__                   %d"NEWLINE, info.FLOATL_MIN_10_EXP__     );
        fprintf(output, "#define __FLOATL_MAX_10_EXP__                   %u"NEWLINE, info.FLOATL_MAX_10_EXP__     );

        fprintf(output, "#define __FLOATL_CONST_0__                      "); floatl_cfg_const_out(output, floatl_cfg_const_0(&info)          );
        fprintf(output, "#define __FLOATL_CONST_1__                      "); floatl_cfg_const_out(output, floatl_cfg_const_1(&info)          );
        fprintf(output, "#define __FLOATL_CONST_1e0__                    "); floatl_cfg_const_out(output, floatl_cfg_const_1eX(&info,0)      );
        fprintf(output, "#define __FLOATL_CONST_1e1__                    "); floatl_cfg_const_out(output, floatl_cfg_const_1eX(&info,1)      );
        fprintf(output, "#define __FLOATL_CONST_1e2__                    "); floatl_cfg_const_out(output, floatl_cfg_const_1eX(&info,2)      );
        fprintf(output, "#define __FLOATL_CONST_1e3__                    "); floatl_cfg_const_out(output, floatl_cfg_const_1eX(&info,3)      );
        fprintf(output, "#define __FLOATL_CONST_1e4__                    "); floatl_cfg_const_out(output, floatl_cfg_const_1eX(&info,4)      );
        fprintf(output, "#define __FLOATL_CONST_1e5__                    "); floatl_cfg_const_out(output, floatl_cfg_const_1eX(&info,5)      );
        fprintf(output, "#define __FLOATL_CONST_1e6__                    "); floatl_cfg_const_out(output, floatl_cfg_const_1eX(&info,6)      );
        fprintf(output, "#define __FLOATL_CONST_1e7__                    "); floatl_cfg_const_out(output, floatl_cfg_const_1eX(&info,7)      );
        fprintf(output, "#define __FLOATL_CONST_1e8__                    "); floatl_cfg_const_out(output, floatl_cfg_const_1eX(&info,8)      );
        fprintf(output, "#define __FLOATL_CONST_1e9__                    "); floatl_cfg_const_out(output, floatl_cfg_const_1eX(&info,9)      );
        fprintf(output, "#define __FLOATL_CONST_1e10__                   "); floatl_cfg_const_out(output, floatl_cfg_const_1eX(&info,10)     );
        fprintf(output, "#define __FLOATL_CONST_1e11__                   "); floatl_cfg_const_out(output, floatl_cfg_const_1eX(&info,11)     );
        fprintf(output, "#define __FLOATL_CONST_1e12__                   "); floatl_cfg_const_out(output, floatl_cfg_const_1eX(&info,12)     );
        fprintf(output, "#define __FLOATL_CONST_1e13__                   "); floatl_cfg_const_out(output, floatl_cfg_const_1eX(&info,13)     );
        fprintf(output, "#define __FLOATL_CONST_1e14__                   "); floatl_cfg_const_out(output, floatl_cfg_const_1eX(&info,14)     );
        fprintf(output, "#define __FLOATL_CONST_1e15__                   "); floatl_cfg_const_out(output, floatl_cfg_const_1eX(&info,15)     );
        fprintf(output, "#define __FLOATL_CONST_PI__                     "); floatl_cfg_const_out(output, floatl_cfg_const_pi(&info)         );
        fprintf(output, "#define __FLOATL_CONST_E__                      "); floatl_cfg_const_out(output, floatl_cfg_const_e(&info)          );
        fprintf(output, "#define __FLOATL_INT_ZERO__                     "); floatl_cfg_const_out(output, floatl_cfg_int_zero(&info)         );
        fprintf(output, "#define __FLOATL_INF__                          "); floatl_cfg_const_out(output, floatl_cfg_inf(&info)              );
        fprintf(output, "#define __FLOATL_NAN__                          "); floatl_cfg_const_out(output, floatl_cfg_nan(&info)              );
        fprintf(output, "#define __FLOATL_ALL_SIGN__                     "); floatl_cfg_const_out(output, floatl_cfg_all_sign(&info)         );
        fprintf(output, "#define __FLOATL_ALL_EXP__                      "); floatl_cfg_const_out(output, floatl_cfg_all_exp(&info)          );
        fprintf(output, "#define __FLOATL_ALL_MANT__                     "); floatl_cfg_const_out(output, floatl_cfg_all_mant(&info)         );
        fprintf(output, "#define __FLOATL_ALL_EXP_MANT__                 "); floatl_cfg_const_out(output, floatl_cfg_all_exp_mant(&info)     );
        fprintf(output, "#define __FLOATL_HIDE_MANT_BIT__                "); floatl_cfg_const_out(output, floatl_cfg_hide_mant_bit(&info)    );
        fprintf(output, "#define __FLOATL_MANT_PLUS_MAX__                "); floatl_cfg_const_out(output, floatl_cfg_mant_plus_max(&info)    );
        fprintf(output, "#define __FLOATL_MANT_WHL__                     "); floatl_cfg_const_out(output, floatl_cfg_mant_whole(&info)       );
        fprintf(output, "#define __FLOATL_MIN__                          "); floatl_cfg_const_out(output, floatl_cfg_min(&info)              );
        fprintf(output, "#define __FLOATL_MAX__                          "); floatl_cfg_const_out(output, floatl_cfg_max(&info)              );
        fprintf(output, "#define __FLOATL_EPSILON__                      "); floatl_cfg_const_out(output, floatl_cfg_epsilon(&info)          );
    }

    fprintf(output, "#endif"NEWLINE);

    fprintf(output, "/*****************************************************************/"NEWLINE);
    fprintf(output, "/*                          Config end                           */"NEWLINE);
    fprintf(output, "/*****************************************************************/"NEWLINE);

    printf("\r\n\r\n-------------------------------------------------------------\r\n\r\n");

    if (sizeof(temp) != (bits >> 3))
    {
        printf("[TODO] Apply the current configuration and run it again to get the new `FLOATL_MAX_DEC`\r\n");
    }
    else  
    {
        printf("[INFO] The configuration has been generated, copy it to the `floatl_cfg.h` range specified\r\n");
    }
}

/*----------------------------------------------------------------------------------*/
/*                           Generate configuration [END]                           */
/*----------------------------------------------------------------------------------*/

static void test_error()
{
    double d1 = 123.456;
    double d2 = 999.365;
    double d3 = d1 + d2;

    floatl f1 = floatl(d1);
    floatl f2 = floatl(d2);
    floatl f3 = floatl_add(f1, f2);

    printf("----------------\r\n");
    double a = 0x8.153800p-134;
    printf("a: %a\r\n", a); // print 0x1.02a700p-131
    snprintf(buffer, sizeof(buffer), "%a", a);
    printf("b: %s\r\n", buffer); // print 0x8.1538p-134
    // why?

    // // 1234567890
    // floatl a = floatl_from("0x1.26580B48p+30"); // 1 001001100101100000001011010010 00

    // floatl a = FLOATL_CONST_0; 
    
    // FLINFO info;
    // floatl_cfg_info_init(&info, sizeof(floatl) * 8);

    // 1234567890
    // 1 001001100101100000001011010010 00
    // floatl_cfg_set_exp(&info, 30);
    // floatl_cfg_set_mant(&info, "001001100101100000001011010010");

    // 123456789012345678901234567890
    // 1 100011101110100100001111111101101100001101110011111000001110111001001110001111110000101011010010
    // floatl_cfg_set_exp(&info, 96);
    // floatl_cfg_set_mant(&info, "100011101110100100001111111101101100001101110011111000001110111001001110001111110000101011010010");

    // a = *((floatl *)(info.buffer));

    // double_print(stdout, d1);
    // printf("double_from_string %f\r\n", double_from_string("123.456"));
    // printf("double_from_string %e\r\n", double_from_string("1.23456e+2"));
    // printf("double_from_string %a\r\n", double_from_string("0x1.23456p+2"));

    printf("%020a\r\n", d1);
    printf("%s\r\n", floatl_show(f1, buffer, sizeof(buffer), "020a"));
    printf("from %s\r\n", floatl_show(floatl_from("123.456"), buffer, sizeof(buffer), "%f"));
    printf("from %s\r\n", floatl_show(floatl_from("1.23456e+2"), buffer, sizeof(buffer), "%e"));

    printf("------------\r\n");
    d1 = 0x1.43a2073p+1;
    printf("%020e\r\n", d1);
    printf("%s\r\n", floatl_show(floatl_from("0x1.43a2073p+1"), buffer, sizeof(buffer), "%020e"));

    if (memcmp(&d3, &f3, sizeof(d3)))
    {
        double_print(stdout, d1);
        double_print(stdout, d2);
        double_print(stdout, d3);
        double_print(stdout, *(double *)(&f3));
    }
}

static void test_define(void)
{
    floatl a = floatl(0.0);
    floatl b = floatl(10.0);
    floatl c = floatl(-3.14);
    floatl d = floatl(1.23456789e+10);
    floatl e = floatl(0x1.23456789p+10);

    floatl f = floatl_from("0.0");
    floatl g = floatl_from("10.0");
    floatl h = floatl_from("-3.14");
    floatl i = floatl_from("1.23456789e+10");
    floatl j = floatl_from("0x1.23456789p+10");

    floatl zero = FLOATL_CONST_0;
    floatl one = FLOATL_CONST_1;
    floatl ten = FLOATL_CONST_10;

    printf("size %d\r\n", sizeof(floatl));
}

static void test_print(void)
{
    floatl a = floatl_from("1234567890.123456789");

    printf("a %s\r\n", floatl_show(a, buffer, sizeof(buffer), "%f"));
    printf("a %s\r\n", floatl_show(a, buffer, sizeof(buffer), "%.2f"));
    printf("a %s\r\n", floatl_show(a, buffer, sizeof(buffer), "%020.2f"));

    printf("a %s\r\n", floatl_show(a, buffer, sizeof(buffer), "%+.6a"));
    printf("a %s\r\n", floatl_show(a, buffer, sizeof(buffer), "%+20.6a"));

    printf("a %s\r\n", floatl_show(a, buffer, sizeof(buffer), "%-20.6e"));
}

static void test_calculate(void)
{
    floatl a = floatl_from("12345678901234567890.123456789");
    floatl b = floatl_from("98765432109876543210.987654321");

    printf("a %s\r\n", floatl_show(a, buffer, sizeof(buffer), "%f"));
    printf("b %s\r\n", floatl_show(b, buffer, sizeof(buffer), "%f"));
    
    printf("a + b: %s\r\n", floatl_show(floatl_add(a, b), buffer, sizeof(buffer), "%f"));
    printf("a - b: %s\r\n", floatl_show(floatl_sub(a, b), buffer, sizeof(buffer), "%f"));
    printf("a * b: %s\r\n", floatl_show(floatl_mul(a, b), buffer, sizeof(buffer), "%f"));
    printf("a / b: %s\r\n", floatl_show(floatl_div(a, b), buffer, sizeof(buffer), "%f"));
    printf("-a: %s\r\n", floatl_show(floatl_neg(a), buffer, sizeof(buffer), "%f"));
    printf("|a|: %s\r\n", floatl_show(floatl_abs(a), buffer, sizeof(buffer), "%f"));
    printf("a > b: %d\r\n", floatl_gt(a, b));
    printf("a >= b: %d\r\n", floatl_ge(a, b));
    printf("a < b: %d\r\n", floatl_lt(a, b));
    printf("a <= b: %d\r\n", floatl_le(a, b));
    printf("a == b: %d\r\n", floatl_eq(a, b));
    printf("a != b: %d\r\n", floatl_ne(a, b));
}

static void test_base(void)
{
    float s1 = 12.625;
    float s2 = 16.456;
    double d1 = 12.625;
    double d2 = 16.456;
    floatl f1 = floatl_from_d(d1);
    floatl f2 = floatl_from_d(d2);

    float s;
    double d;
    floatl f;
    float temp;

    printf("==============================================\r\n");
    printf("==================== + =======================\r\n");
    printf("==============================================\r\n");

    s = s1 + s2;
    d = d1 + d2;
    f = floatl_add(f1, f2);

    float_show_raw(s);
    double_show_raw(d);
    floatl_show_raw(f, 0, 0, 0);

    printf("==============================================\r\n");
    printf("==================== - =======================\r\n");
    printf("==============================================\r\n");

    s = s1 - s2;
    d = d1 - d2;
    f = floatl_sub(f1, f2);

    float_show_raw(s);
    double_show_raw(d);
    floatl_show_raw(f, 0, 0, 0);

    printf("==============================================\r\n");
    printf("==================== * =======================\r\n");
    printf("==============================================\r\n");

    s = s1 * s2;
    d = d1 * d2;
    f = floatl_mul(f1, f2);

    float_show_raw(s);
    double_show_raw(d);
    floatl_show_raw(f, 0, 0, 0);

    printf("==============================================\r\n");
    printf("==================== / =======================\r\n");
    printf("==============================================\r\n");

    s = s1 / s2;
    d = d1 / d2;
    f = floatl_div(f1, f2);

    float_show_raw(s);
    double_show_raw(d);
    floatl_show_raw(f, 0, 0, 0);

    // float l1 = 1.0f;
    // showBits(&l1, sizeof(l1));

#if 0
    // float_show_raw(0.0f);
    // float_show_raw(1.0f);

    // double y = 11.6250;
    // int e = 0;
    // double_show_raw(y);
    // y = frexp(y, &e);
    // double_show_raw(y);
    // printf("y %f e %d\r\n", y, e);
#endif 
    double v = -3.14;
    printf("ceil %f\r\n", ceil(v));
    printf("floor %f\r\n", floor(v));
    printf("round %f\r\n", round(v));

    printf("double_ceil %f\r\n", double_ceil(v));
    printf("double_floor %f\r\n", double_floor(v));
    printf("double_round %f\r\n", double_round(v));

    printf("floatl_ceil %s\r\n", floatl_show(floatl_ceil(floatl(v)), buffer, sizeof(buffer), "%f"));
    printf("floatl_floor %s\r\n", floatl_show(floatl_floor(floatl(v)), buffer, sizeof(buffer), "%f"));
    printf("floatl_round %s\r\n", floatl_show(floatl_round(floatl(v)), buffer, sizeof(buffer), "%f"));

    printf("to_d %f\r\n", floatl_to_d(FLOATL_PI));
}

/************************************************************************************/
/*************************************  Command  ************************************/
/************************************************************************************/

static void usage(void)
{
    printf(
"Usage: floatl [opt] [arg] ...\n"
"\n"
"options:\n"
"    -e <execute>        Specifies the function to execute, the default is the <base> test\n"
"                        <base>      Test base function\n"
"                        <ut>        Unit test\n"
"                        <define>    Test define function\n"
"                        <cal>       Calculate string math expression\n"
"                        <gen>       Generate the floatl configuration file code segment, need specify -f -b\n"
"                        <print>     Print an floatl number\n"
"                        <error>     Function that tests for floatl errors\n"
"    -l <format>         Format string, 10d, 08x, ...\n"
"    -o <op function>    Operate function, add, sub, mul, div, ...\n"
"    -n <floatl>           floatl number expression\n"
"    -f <filename>       File name, temporarily store configuration code segment\n"
"    -b <bits>           Maximum number of configured bits\n"
"    -h                  Print help\n"
"    -v                  Print version\n"
"    -u [<period>]       Unit test period, unit ms, the default is 1000ms\n"
"\n"

    );
}

static int test(int argc, char *argv[])
{
    char *execute = NULL;
    int ut_period = 1000;
    char *filename = NULL;
    int bits = 0;

    /* reset getopt */
    command_opt_init();

    while (1)
    {
        int opt = command_getopt(argc, argv, "e:hvu::f:b:n:o:l:");
        if (opt == -1) break;

        switch (opt) 
        {
        case 'b' :
            bits = atoi(command_optarg);
            break;
        case 'f' :
            filename = command_optarg;
            break;
        case 'u' :
            if (command_optarg) ut_period = atoi(command_optarg);
            break;
        case 'e' :
            execute = command_optarg;
            break;
        case 'v' :
            printf("floatl version %d.%d.%d\r\n", FLOATL_V_MAJOR, FLOATL_V_MINOR, FLOATL_V_PATCH);
            return 0;
        case '?':
            printf("Unknown option `%c`\r\n", command_optopt);
            return -1;
        case 'h' : 
        default:
            usage();
            return 0;
        }
    }

    if (execute)
    {
        if (!strcmp(execute, "base"))
        {
            test_base();
        }
        else if (!strcmp(execute, "ut"))
        {
            srand((uint32_t)time(NULL));
            #if defined(TEST_TARGET_floatl)
            while (1)
            {
                unitt_task();
                usleep(1000 * ut_period);
            }
            #else  
            printf("create task %d\r\n", task_create(ut_period, unitt_task));
            #endif
        }
        else if (!strcmp(execute, "define"))
        {
            test_define();
        }
        else if (!strcmp(execute, "gen"))
        {
            floatl_cfg_generate(bits, filename);
        }
        else if (!strcmp(execute, "cal"))
        {
            test_calculate();
        }
        else if (!strcmp(execute, "print"))
        {
            test_print();
        }
        else if (!strcmp(execute, "error"))
        {
            test_error();
        }
    }
    else  
    {
        test_base();
    }
    
    return 0;
}

/************************************************************************************/
/************************************ Test entry ************************************/
/************************************************************************************/

#if defined(TEST_TARGET_floatl)
int main(int argc, char *argv[])
{
    return test(argc, argv);
}
#else 
void test_floatl(void)
{
    command_export("floatl", test);
}
init_export_app(test_floatl);
#endif 
